{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0acdc7c0-61bd-45a1-a040-df05c67ab0b2",
   "metadata": {},
   "source": [
    "# Multiagent Pattern - Multiagent Collaboration"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ba71f3e-5421-4dd7-bc63-e3fcdf2680a6",
   "metadata": {},
   "source": [
    "source : https://github.com/neural-maze/agentic_patterns\n",
    "\n",
    "You may have heard about frameworks like [CrewAI](https://www.crewai.com/) or [AutoGen](https://microsoft.github.io/autogen/0.2/), which allow you to create multi-agent applications.\n",
    "\n",
    "These frameworks implement different variations of the multi-agent pattern, in which tasks are divided into **smaller subtasks executed by different roles** (e.g. one agent can be a software engineer, another a project manager, etc.)\n",
    "\n",
    "For this final lesson, I wanted to build something more elaborate. That's why I've been working on a 𝐦𝐢𝐧𝐢𝐦𝐚𝐥𝐢𝐬𝐭 𝐯𝐞𝐫𝐬𝐢𝐨𝐧 𝐨𝐟 𝐂𝐫𝐞𝐰𝐀𝐈, drawing inspiration from two of its key concepts: 𝐂𝐫𝐞𝐰 and 𝐀𝐠𝐞𝐧𝐭.\n",
    "\n",
    "Additionally, I've also borrowed ideas from [𝐀𝐢𝐫𝐟𝐥𝐨𝐰](https://airflow.apache.org/)'𝐬 𝐝𝐞𝐬𝐢𝐠𝐧 𝐩𝐡𝐢𝐥𝐨𝐬𝐨𝐩𝐡𝐲, using >> and << to define dependencies between my agents. In this micro-CrewAI, 𝐚𝐠𝐞𝐧𝐭𝐬 are equivalent to 𝐀𝐢𝐫𝐟𝐥𝐨𝐰 𝐓𝐚𝐬𝐤𝐬 and the 𝐂𝐫𝐞𝐰 is equivalent to an 𝐀𝐢𝐫𝐟𝐥𝐨𝐰 𝐃𝐀𝐆.\n",
    "\n",
    "Take a look at the previous lessons if you haven't!\n",
    "\n",
    "* [First Lesson: The Reflection Pattern](https://github.com/neural-maze/agentic_patterns/blob/main/notebooks/reflection_pattern.ipynb)\n",
    "* [Second Lesson: The Tool Pattern](https://github.com/neural-maze/agentic_patterns/blob/main/notebooks/tool_pattern.ipynb)\n",
    "* [Third Lesson: The Planning Pattern](https://github.com/neural-maze/agentic_patterns/blob/main/notebooks/planning_pattern.ipynb)\n",
    "\n",
    "**Let's begin!!** 💪"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f786e114-cde4-400e-8781-fb7f48bd072c",
   "metadata": {},
   "source": [
    "## The Agent Class"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec27b59f-82c6-4b21-917c-fa6d64c1e614",
   "metadata": {},
   "source": [
    "First of all, we need an **Agent Class**. This class implements an\n",
    "Agent, and internally it implements the ReAct technique (check [Lesson 3](https://github.com/neural-maze/agentic_patterns/blob/main/notebooks/planning_pattern.ipynb) if you want to see this technique in detail!)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f81fe6b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from textwrap import dedent\n",
    "from collections import deque\n",
    "\n",
    "from colorama import Fore\n",
    "from graphviz import Digraph  # type: ignore\n",
    "\n",
    "from agentic_patterns.utils.logging import fancy_print\n",
    "\n",
    "class Agent:\n",
    "    \"\"\"\n",
    "    Represents an AI agent that can work as part of a team to complete tasks.\n",
    "\n",
    "    This class implements an agent with dependencies, context handling, and task execution capabilities.\n",
    "    It can be used in a multi-agent system where agents collaborate to solve complex problems.\n",
    "\n",
    "    Attributes:\n",
    "        name (str): The name of the agent.\n",
    "        backstory (str): The backstory or background of the agent.\n",
    "        task_description (str): A description of the task assigned to the agent.\n",
    "        task_expected_output (str): The expected format or content of the task output.\n",
    "        react_agent (ReactAgent): An instance of ReactAgent used for generating responses.\n",
    "        dependencies (list[Agent]): A list of Agent instances that this agent depends on.\n",
    "        dependents (list[Agent]): A list of Agent instances that depend on this agent.\n",
    "        context (str): Accumulated context information from other agents.\n",
    "\n",
    "    Args:\n",
    "        name (str): The name of the agent.\n",
    "        backstory (str): The backstory or background of the agent.\n",
    "        task_description (str): A description of the task assigned to the agent.\n",
    "        task_expected_output (str, optional): The expected format or content of the task output. Defaults to \"\".\n",
    "        tools (list[Tool] | None, optional): A list of Tool instances available to the agent. Defaults to None.\n",
    "        llm (str, optional): The name of the language model to use. Defaults to \"llama-3.1-70b-versatile\".\n",
    "    \"\"\"\n",
    "\n",
    "    def __init__(\n",
    "        self,\n",
    "        name: str,\n",
    "        backstory: str,\n",
    "        task_description: str,\n",
    "        task_expected_output: str = \"\",\n",
    "        tools: list[Tool] | None = None,\n",
    "        llm: str = \"llama-3.1-70b-versatile\",\n",
    "    ):\n",
    "        self.name = name\n",
    "        self.backstory = backstory\n",
    "        self.task_description = task_description\n",
    "        self.task_expected_output = task_expected_output\n",
    "        self.react_agent = ReactAgent(\n",
    "            model=llm, system_prompt=self.backstory, tools=tools or []\n",
    "        )\n",
    "\n",
    "        self.dependencies: list[Agent] = []  # Agents that this agent depends on\n",
    "        self.dependents: list[Agent] = []  # Agents that depend on this agent\n",
    "\n",
    "        self.context = \"\"\n",
    "\n",
    "        # Automatically register this agent to the active Crew context if one exists\n",
    "        Crew.register_agent(self)\n",
    "\n",
    "    def __repr__(self):\n",
    "        return f\"{self.name}\"\n",
    "\n",
    "    def __rshift__(self, other):\n",
    "        \"\"\"\n",
    "        Defines the '>>' operator. This operator is used to indicate agent dependency.\n",
    "\n",
    "        Args:\n",
    "            other (Agent): The agent that depends on this agent.\n",
    "        \"\"\"\n",
    "        self.add_dependent(other)\n",
    "        return other  # Allow chaining\n",
    "\n",
    "    def __lshift__(self, other):\n",
    "        \"\"\"\n",
    "        Defines the '<<' operator to indicate agent dependency in reverse.\n",
    "\n",
    "        Args:\n",
    "            other (Agent): The agent that this agent depends on.\n",
    "\n",
    "        Returns:\n",
    "            Agent: The `other` agent to allow for chaining.\n",
    "        \"\"\"\n",
    "        self.add_dependency(other)\n",
    "        return other  # Allow chaining\n",
    "\n",
    "    def __rrshift__(self, other):\n",
    "        \"\"\"\n",
    "        Defines the '<<' operator.This operator is used to indicate agent dependency.\n",
    "\n",
    "        Args:\n",
    "            other (Agent): The agent that this agent depends on.\n",
    "        \"\"\"\n",
    "        self.add_dependency(other)\n",
    "        return self  # Allow chaining\n",
    "\n",
    "    def __rlshift__(self, other):\n",
    "        \"\"\"\n",
    "        Defines the '<<' operator when evaluated from right to left.\n",
    "        This operator is used to indicate agent dependency in the normal order.\n",
    "\n",
    "        Args:\n",
    "            other (Agent): The agent that depends on this agent.\n",
    "\n",
    "        Returns:\n",
    "            Agent: The current agent (self) to allow for chaining.\n",
    "        \"\"\"\n",
    "        self.add_dependent(other)\n",
    "        return self  # Allow chaining\n",
    "\n",
    "    def add_dependency(self, other):\n",
    "        \"\"\"\n",
    "        Adds a dependency to this agent.\n",
    "\n",
    "        Args:\n",
    "            other (Agent | list[Agent]): The agent(s) that this agent depends on.\n",
    "\n",
    "        Raises:\n",
    "            TypeError: If the dependency is not an Agent or a list of Agents.\n",
    "        \"\"\"\n",
    "        if isinstance(other, Agent):\n",
    "            self.dependencies.append(other)\n",
    "            other.dependents.append(self)\n",
    "        elif isinstance(other, list) and all(isinstance(item, Agent) for item in other):\n",
    "            for item in other:\n",
    "                self.dependencies.append(item)\n",
    "                item.dependents.append(self)\n",
    "        else:\n",
    "            raise TypeError(\"The dependency must be an instance or list of Agent.\")\n",
    "\n",
    "    def add_dependent(self, other):\n",
    "        \"\"\"\n",
    "        Adds a dependent to this agent.\n",
    "\n",
    "        Args:\n",
    "            other (Agent | list[Agent]): The agent(s) that depend on this agent.\n",
    "\n",
    "        Raises:\n",
    "            TypeError: If the dependent is not an Agent or a list of Agents.\n",
    "        \"\"\"\n",
    "        if isinstance(other, Agent):\n",
    "            other.dependencies.append(self)\n",
    "            self.dependents.append(other)\n",
    "        elif isinstance(other, list) and all(isinstance(item, Agent) for item in other):\n",
    "            for item in other:\n",
    "                item.dependencies.append(self)\n",
    "                self.dependents.append(item)\n",
    "        else:\n",
    "            raise TypeError(\"The dependent must be an instance or list of Agent.\")\n",
    "\n",
    "    def receive_context(self, input_data):\n",
    "        \"\"\"\n",
    "        Receives and stores context information from other agents.\n",
    "\n",
    "        Args:\n",
    "            input_data (str): The context information to be added.\n",
    "        \"\"\"\n",
    "        self.context += f\"{self.name} received context: \\n{input_data}\"\n",
    "\n",
    "    def create_prompt(self):\n",
    "        \"\"\"\n",
    "        Creates a prompt for the agent based on its task description, expected output, and context.\n",
    "\n",
    "        Returns:\n",
    "            str: The formatted prompt string.\n",
    "        \"\"\"\n",
    "        prompt = dedent(\n",
    "            f\"\"\"\n",
    "        You are an AI agent. You are part of a team of agents working together to complete a task.\n",
    "        I'm going to give you the task description enclosed in <task_description></task_description> tags. I'll also give\n",
    "        you the available context from the other agents in <context></context> tags. If the context\n",
    "        is not available, the <context></context> tags will be empty. You'll also receive the task\n",
    "        expected output enclosed in <task_expected_output></task_expected_output> tags. With all this information\n",
    "        you need to create the best possible response, always respecting the format as describe in\n",
    "        <task_expected_output></task_expected_output> tags. If expected output is not available, just create\n",
    "        a meaningful response to complete the task.\n",
    "\n",
    "        <task_description>\n",
    "        {self.task_description}\n",
    "        </task_description>\n",
    "\n",
    "        <task_expected_output>\n",
    "        {self.task_expected_output}\n",
    "        </task_expected_output>\n",
    "\n",
    "        <context>\n",
    "        {self.context}\n",
    "        </context>\n",
    "\n",
    "        Your response:\n",
    "        \"\"\"\n",
    "        ).strip()\n",
    "\n",
    "        return prompt\n",
    "\n",
    "    def run(self):\n",
    "        \"\"\"\n",
    "        Runs the agent's task and generates the output.\n",
    "\n",
    "        This method creates a prompt, runs it through the ReactAgent, and passes the output to all dependent agents.\n",
    "\n",
    "        Returns:\n",
    "            str: The output generated by the agent.\n",
    "        \"\"\"\n",
    "        msg = self.create_prompt()\n",
    "        output = self.react_agent.run(user_msg=msg)\n",
    "\n",
    "        # Pass the output to all dependents\n",
    "        for dependent in self.dependents:\n",
    "            dependent.receive_context(output)\n",
    "        return output\n",
    "    \n",
    "\n",
    "\n",
    "\n",
    "class Crew:\n",
    "    \"\"\"\n",
    "    A class representing a crew of agents working together.\n",
    "\n",
    "    This class manages a group of agents, their dependencies, and provides methods\n",
    "    for running the agents in a topologically sorted order.\n",
    "\n",
    "    Attributes:\n",
    "        current_crew (Crew): Class-level variable to track the active Crew context.\n",
    "        agents (list): A list of agents in the crew.\n",
    "    \"\"\"\n",
    "\n",
    "    current_crew = None\n",
    "\n",
    "    def __init__(self):\n",
    "        self.agents = []\n",
    "\n",
    "    def __enter__(self):\n",
    "        \"\"\"\n",
    "        Enters the context manager, setting this crew as the current active context.\n",
    "\n",
    "        Returns:\n",
    "            Crew: The current Crew instance.\n",
    "        \"\"\"\n",
    "        Crew.current_crew = self\n",
    "        return self\n",
    "\n",
    "    def __exit__(self, exc_type, exc_val, exc_tb):\n",
    "        \"\"\"\n",
    "        Exits the context manager, clearing the active context.\n",
    "\n",
    "        Args:\n",
    "            exc_type: The exception type, if an exception was raised.\n",
    "            exc_val: The exception value, if an exception was raised.\n",
    "            exc_tb: The traceback, if an exception was raised.\n",
    "        \"\"\"\n",
    "        Crew.current_crew = None\n",
    "\n",
    "    def add_agent(self, agent):\n",
    "        \"\"\"\n",
    "        Adds an agent to the crew.\n",
    "\n",
    "        Args:\n",
    "            agent: The agent to be added to the crew.\n",
    "        \"\"\"\n",
    "        self.agents.append(agent)\n",
    "\n",
    "    @staticmethod\n",
    "    def register_agent(agent):\n",
    "        \"\"\"\n",
    "        Registers an agent with the current active crew context.\n",
    "\n",
    "        Args:\n",
    "            agent: The agent to be registered.\n",
    "        \"\"\"\n",
    "        if Crew.current_crew is not None:\n",
    "            Crew.current_crew.add_agent(agent)\n",
    "\n",
    "    def topological_sort(self):\n",
    "        \"\"\"\n",
    "        Performs a topological sort of the agents based on their dependencies.\n",
    "\n",
    "        Returns:\n",
    "            list: A list of agents sorted in topological order.\n",
    "\n",
    "        Raises:\n",
    "            ValueError: If there's a circular dependency among the agents.\n",
    "        \"\"\"\n",
    "        in_degree = {agent: len(agent.dependencies) for agent in self.agents}\n",
    "        queue = deque([agent for agent in self.agents if in_degree[agent] == 0])\n",
    "\n",
    "        sorted_agents = []\n",
    "\n",
    "        while queue:\n",
    "            current_agent = queue.popleft()\n",
    "            sorted_agents.append(current_agent)\n",
    "\n",
    "            for dependent in current_agent.dependents:\n",
    "                in_degree[dependent] -= 1\n",
    "                if in_degree[dependent] == 0:\n",
    "                    queue.append(dependent)\n",
    "\n",
    "        if len(sorted_agents) != len(self.agents):\n",
    "            raise ValueError(\n",
    "                \"Circular dependencies detected among agents, preventing a valid topological sort\"\n",
    "            )\n",
    "\n",
    "        return sorted_agents\n",
    "\n",
    "    def plot(self):\n",
    "        \"\"\"\n",
    "        Plots the Directed Acyclic Graph (DAG) of agents in the crew using Graphviz.\n",
    "\n",
    "        Returns:\n",
    "            Digraph: A Graphviz Digraph object representing the agent dependencies.\n",
    "        \"\"\"\n",
    "        dot = Digraph(format=\"png\")  # Set format to PNG for inline display\n",
    "\n",
    "        # Add nodes and edges for each agent in the crew\n",
    "        for agent in self.agents:\n",
    "            dot.node(agent.name)\n",
    "            for dependency in agent.dependencies:\n",
    "                dot.edge(dependency.name, agent.name)\n",
    "        return dot\n",
    "\n",
    "    def run(self):\n",
    "        \"\"\"\n",
    "        Runs all agents in the crew in topologically sorted order.\n",
    "\n",
    "        This method executes each agent's run method and prints the results.\n",
    "        \"\"\"\n",
    "        sorted_agents = self.topological_sort()\n",
    "        for agent in sorted_agents:\n",
    "            fancy_print(f\"RUNNING AGENT: {agent}\")\n",
    "            print(Fore.RED + f\"{agent.run()}\")\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ba412fb-9604-4146-93bb-ed0451c529f8",
   "metadata": {},
   "source": [
    "Let's create some example agent, to see how it works."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3aa589d5-7825-4fe0-a269-f35474bf6da5",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_example = Agent(\n",
    "    name=\"Poet Agent\",\n",
    "    backstory=\"You are a well-known poet, who enjoys creating high quality poetry.\",\n",
    "    task_description=\"Write a poem about the meaning of life\",\n",
    "    task_expected_output=\"Just output the poem, without any title or introductory sentences\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fd71e1e3-893f-40e1-b6c5-f15e42beb851",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(agent_example.run())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a8e0e4aa-cce0-4648-a6fd-eac244662874",
   "metadata": {},
   "source": [
    "You can also associate tools with the agent. Let's create a tool for writing some string into a CSV."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7fae777-3bf6-4be5-8ab8-de79f545d25a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from agentic_patterns.tool_pattern.tool import tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4016dc0d-cc7f-4e7a-b971-2ecfd31b79cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "@tool\n",
    "def write_str_to_txt(string_data: str, txt_filename: str):\n",
    "    \"\"\"\n",
    "    Writes a string to a txt file.\n",
    "\n",
    "    This function takes a string and writes it to a text file. If the file already exists, \n",
    "    it will be overwritten with the new data.\n",
    "\n",
    "    Args:\n",
    "        string_data (str): The string containing the data to be written to the file.\n",
    "        txt_filename (str): The name of the text file to which the data should be written.\n",
    "    \"\"\"\n",
    "    # Write the string data to the text file\n",
    "    with open(txt_filename, mode='w', encoding='utf-8') as file:\n",
    "        file.write(string_data)\n",
    "\n",
    "    print(f\"Data successfully written to {txt_filename}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ed9d5c5-6a1a-4510-85a1-5c03c2d2fa37",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_tool_example = Agent(\n",
    "    name=\"Writer Agent\",\n",
    "    backstory=\"You are a language model specialised in writing text into .txt files\",\n",
    "    task_description=\"Write the string 'This is a Tool Agent' into './tool_agent_example.txt'\",\n",
    "    task_expected_output=\"A .txt file containing the given string\",\n",
    "    tools=write_str_to_txt,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6214dc3-e24f-430c-99f0-6a8fe484d8ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_tool_example.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "026c6229-5833-4a99-afa6-e45cc72fb04d",
   "metadata": {},
   "source": [
    "## Defining Agent Dependencies"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab6688e9-1fdf-4b51-bb33-c63c80ce9bb7",
   "metadata": {},
   "source": [
    "Let's define two agents now."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bf142278-b556-42cb-9a5e-2761af96f5c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_1 = Agent(\n",
    "    name=\"Poet Agent\",\n",
    "    backstory=\"You are a well-known poet, who enjoys creating high quality poetry.\",\n",
    "    task_description=\"Write a poem about the meaning of life\",\n",
    "    task_expected_output=\"Just output the poem, without any title or introductory sentences\",\n",
    ")\n",
    "\n",
    "agent_2 = Agent(\n",
    "    name=\"Poem Translator Agent\",\n",
    "    backstory=\"You are an expert translator especially skilled in Ancient Greek\",\n",
    "    task_description=\"Translate a poem into Ancient Greek\", \n",
    "    task_expected_output=\"Just output the translated poem and nothing else\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "565d174d-ab42-4318-b53a-1ed142512f4f",
   "metadata": {},
   "source": [
    "We can define the agent dependencies using the `>>` operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f52347ac-99b3-4695-a6a4-cf31f7784826",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_1 >> agent_2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "603e5a0d-25ef-4fdf-8ed8-63bd50f90cfd",
   "metadata": {},
   "source": [
    "This means `agent_2` depends on `agent_1`. We can check the dependencies and dependents of both agents."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "560148d0-ae20-45ba-b22f-938eda0be64d",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Agent 1 dependencies: \", agent_1.dependencies)\n",
    "print(\"Agent 1 dependents: \", agent_1.dependents)\n",
    "print(\"Agent 2 dependencies: \", agent_2.dependencies)\n",
    "print(\"Agent 2 dependents: \", agent_2.dependents)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9276064-136c-49bc-ae95-4d2b2ba47187",
   "metadata": {},
   "source": [
    "Now, if we run `agent_1`, the results will be added to `agent_2`'s context."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26f58fda-3673-4067-ad0b-a4055a476a32",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(agent_1.run())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cd3dccc9-c201-4a70-bebf-1b9b83b7f59d",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(agent_2.context)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4019dd7-b79f-4595-8104-9a47c64e4ba4",
   "metadata": {},
   "source": [
    "Now, if we run the second agent, it will use the context received from the previous agent to generate its output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a686cefc-db4f-4eb4-9e93-11bdbdd30d6d",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(agent_2.run())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb11c5a8-fc37-42e3-a521-8cf87ae86dbc",
   "metadata": {},
   "source": [
    "## The Crew"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "66c83cc4-87cf-4afc-855b-e5263ad95a71",
   "metadata": {},
   "outputs": [],
   "source": [
    "from agentic_patterns.multiagent_pattern.crew import Crew"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e58d1cbd-3dbb-4a36-8fc8-9b4242a7b1d3",
   "metadata": {},
   "outputs": [],
   "source": [
    "with Crew() as crew:\n",
    "    agent_1 = Agent(\n",
    "        name=\"Poet Agent\",\n",
    "        backstory=\"You are a well-known poet, who enjoys creating high quality poetry.\",\n",
    "        task_description=\"Write a poem about the meaning of life\",\n",
    "        task_expected_output=\"Just output the poem, without any title or introductory sentences\",\n",
    "    )\n",
    "\n",
    "    agent_2 = Agent(\n",
    "        name=\"Poem Translator Agent\",\n",
    "        backstory=\"You are an expert translator especially skilled in Spanish\",\n",
    "        task_description=\"Translate a poem into Spanish\", \n",
    "        task_expected_output=\"Just output the translated poem and nothing else\"\n",
    "    )\n",
    "\n",
    "    agent_3 = Agent(\n",
    "        name=\"Writer Agent\",\n",
    "        backstory=\"You are an expert transcriber, that loves writing poems into txt files\",\n",
    "        task_description=\"You'll receive a Spanish poem in your context. You need to write the poem into './poem.txt' file\",\n",
    "        task_expected_output=\"A txt file containing the greek poem received from the context\",\n",
    "        tools=write_str_to_txt,\n",
    "    )\n",
    "\n",
    "    agent_1 >> agent_2 >> agent_3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9027cbdc-2bb0-419b-87a0-0352f59d3079",
   "metadata": {},
   "outputs": [],
   "source": [
    "crew.plot()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b39f70a0-2c91-40c4-ab56-8318cef7ef97",
   "metadata": {},
   "outputs": [],
   "source": [
    "crew.run()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
